!
! SYS_SIZE is the number of clicks (16 bytes) to be loaded.
! 0x3000 is 0x30000 bytes = 196kB, more than enough for current
! versions of linux
!
SYSSIZE = 0x3000				! 定义系统大小常量，0x3000表示196KB，足够当前Linux版本使用
!
!	bootsect.s		(C) 1991 Linus Torvalds
!
! bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves
! iself out of the way to address 0x90000, and jumps there.
!
! It then loads 'setup' directly after itself (0x90200), and the system
! at 0x10000, using BIOS interrupts. 
!
! NOTE! currently system is at most 8*65536 bytes long. This should be no
! problem, even in the future. I want to keep it simple. This 512 kB
! kernel size should be enough, especially as this doesn't contain the
! buffer cache as in minix
!
! The loader has been made as simple as possible, and continuos
! read errors will result in a unbreakable loop. Reboot by hand. It
! loads pretty fast by getting whole sectors at a time whenever possible.

.globl begtext, begdata, begbss, endtext, enddata, endbss	! 声明全局符号，定义代码段、数据段、BSS段的开始和结束标记
.text
begtext:							! 代码段开始标记
.data
begdata:							! 数据段开始标记
.bss
begbss:								! BSS段开始标记
.text

SETUPLEN = 4				! setup模块的扇区数（4个扇区）
BOOTSEG  = 0x07c0			! 引导扇区原始地址（0x7c00）
INITSEG  = 0x9000			! 引导扇区移动后的地址（0x90000）
SETUPSEG = 0x9020			! setup模块加载地址（0x90200）
SYSSEG   = 0x1000			! 系统内核加载地址（0x10000）
ENDSEG   = SYSSEG + SYSSIZE		! 加载结束地址

! ROOT_DEV:	0x000 - same type of floppy as boot.
!		0x301 - first partition on first drive etc
ROOT_DEV = 0x306				! 定义根设备号，0x306表示第二个硬盘的第二个分区

entry _start						! 定义程序入口点
_start:							! 程序开始标签
	mov	ax,#BOOTSEG    			! 将引导扇区段地址0x07c0加载到ax寄存器
	mov	ds,ax   				! 设置数据段寄存器ds为0x07c0（源段地址）
	mov	ax,#INITSEG				! 将目标段地址0x9000加载到ax寄存器
	mov	es,ax					! 设置附加段寄存器es为0x9000（目标段地址）
	mov	cx,#256					! 设置计数器cx为256（复制256个字，即512字节）
	sub	si,si					! 源偏移地址si清零
	sub	di,di					! 目标偏移地址di清零  
	rep							! 重复执行下一条指令
	movw						! 移动字指令，将ds:si指向的内容复制到es:di  mov word ptr [es:di], [ds:si]
	jmpi	go,INITSEG			! 跳转到新位置继续执行（0x9000:go）

	
go:	mov	ax,cs					! 将代码段寄存器cs的值加载到ax    这个时候， CS寄存器 = INITSEG = 0x9000
	mov	ds,ax					! 设置数据段寄存器ds为当前代码段   ds=0x9000
	mov	es,ax					! 设置附加段寄存器es为当前代码段   es=0x9000
! put stack at 0x9ff00.
	mov	ss,ax					! 设置栈段寄存器ss为当前代码段
	mov	sp,#0xFF00				! 设置栈指针sp为0xFF00（栈顶地址）

! load the setup-sectors directly after the bootblock.
! Note that 'es' is already set up.

load_setup:						! 加载setup模块的标签
	mov	dx,#0x0000				! 设置dx为0x0000（驱动器0，磁头0）
	mov	cx,#0x0002				! 设置cx为0x0002（扇区2，磁道0）
	mov	bx,#0x0200				! 设置bx为0x0200（目标地址偏移512字节）   
	mov	ax,#0x0200+SETUPLEN		! 设置ax为0x0204（服务2，读取4个扇区）
	int	0x13					! 调用BIOS中断0x13读取磁盘   加载数据到 内存  es:dx 的内存位置，也就是 0x9000:0x0200 处
	jnc	ok_load_setup			! 如果没有进位标志（成功），跳转到ok_load_setup
	mov	dx,#0x0000				! 重置dx为0x0000
	mov	ax,#0x0000				! 重置ax为0x0000（重置磁盘）
	int	0x13					! 调用BIOS中断0x13重置磁盘
	j	load_setup				! 跳转回load_setup重试

ok_load_setup:					! setup模块加载成功后的标签

! Get disk drive parameters, specifically nr of sectors/track

	mov	dl,#0x00				! 设置dl为0x00（驱动器0）
	mov	ax,#0x0800				! 设置ax为0x0800（AH=8是获取驱动器参数）
	int	0x13					! 调用BIOS中断0x13获取驱动器参数
	mov	ch,#0x00				! 将ch清零
	seg cs						! 使用代码段前缀
	mov	sectors,cx				! 将cx中的扇区数保存到sectors变量
	mov	ax,#INITSEG				! 将INITSEG加载到ax
	mov	es,ax					! 设置es为INITSEG

! Print some inane message
	
	call read_cursor				! 调用read_cursor函数读取光标位置
	mov	cx,#2           		! 设置cx为2（显示2个字符）
	mov	bx,#0x0007				! 设置bx为0x0007（页0，属性7，白色）
	mov	bp,#msg1        		! 将msg1字符串地址加载到bp
	mov	ax,#0x1301				! 设置ax为0x1301（写字符串，移动光标）
	int	0x10					! 调用BIOS中断0x10显示字符串

    ! 显示 Jnix
	call read_cursor				! 调用read_cursor函数读取光标位置
	mov cx, #4   				! 设置cx为4（显示4个字符）
	mov bx, #0x0009				! 设置bx为0x0009（页0，属性9，亮蓝色）
	mov bp, #msg1+2				! 将msg1+2地址加载到bp（指向"Jnix"）
	mov ax, #0x1301				! 设置ax为0x1301（写字符串，移动光标）
	int 0x10					! 调用BIOS中断0x10显示字符串
	
	call read_cursor				! 调用read_cursor函数读取光标位置
	mov cx, #16					! 设置cx为16（显示16个字符）
	mov bx, #0x0007				! 设置bx为0x0007（页0，属性7，白色）
	mov bp, #msg1+6				! 将msg1+6地址加载到bp（指向" is loading..."）
	mov ax, #0x1301				! 设置ax为0x1301（写字符串，移动光标）
	int 0x10					! 调用BIOS中断0x10显示字符串

! ok, we've written the message, now
! we want to load the system (at 0x10000)

	mov	ax,#SYSSEG				! 将SYSSEG加载到ax
	mov	es,ax					! 设置es为SYSSEG（系统加载段地址）
	call	read_it				! 调用read_it函数加载系统内核
	call	kill_motor			! 调用kill_motor函数关闭软驱电机

! After that we check which root-device to use. If the device is
! defined (!= 0), nothing is done and the given device is used.
! Otherwise, either /dev/PS0 (2,28) or /dev/at0 (2,8), depending
! on the number of sectors that the BIOS reports currently.

	seg cs						! 使用代码段前缀
	mov	ax,root_dev				! 将root_dev的值加载到ax
	cmp	ax,#0					! 比较ax与0
	jne	root_defined			! 如果不等于0，跳转到root_defined
	seg cs						! 使用代码段前缀
	mov	bx,sectors				! 将sectors的值加载到bx
	mov	ax,#0x0208				! 设置ax为0x0208（/dev/ps0 - 1.2MB软盘）
	cmp	bx,#15					! 比较bx与15
	je	root_defined			! 如果等于15，跳转到root_defined
	mov	ax,#0x021c				! 设置ax为0x021c（/dev/PS0 - 1.44MB软盘）
	cmp	bx,#18					! 比较bx与18
	je	root_defined			! 如果等于18，跳转到root_defined
undef_root:						! 未定义根设备的标签
	jmp undef_root				! 无限循环（错误处理）
root_defined:					! 根设备已定义的标签
	seg cs						! 使用代码段前缀
	mov	root_dev,ax			! 将ax的值保存到root_dev

! after that (everyting loaded), we jump to
! the setup-routine loaded directly after
! the bootblock:

	jmpi	0,SETUPSEG			! 跳转到setup模块继续执行

! This routine loads the system at address 0x10000, making sure
! no 64kB boundaries are crossed. We try to load it as fast as
! possible, loading whole tracks whenever we can.
!
! in:	es - starting address segment (normally 0x1000)
!
sread:	.word 1+SETUPLEN		! 当前磁道已读扇区数（1+SETUPLEN）
head:	.word 0					! 当前磁头号
track:	.word 0					! 当前磁道号

read_it:						! 读取系统内核的主函数
	mov ax,es					! 将es的值加载到ax
	test ax,#0x0fff			! 测试ax的低12位
die:	jne die					! 如果不为0，跳转到die（es必须在64KB边界）
	xor bx,bx					! 将bx清零（段内起始地址）
rp_read:						! 读取循环标签
	mov ax,es					! 将es的值加载到ax
	cmp ax,#ENDSEG				! 比较ax与ENDSEG
	jb ok1_read				! 如果小于ENDSEG，跳转到ok1_read
	ret							! 返回（已加载完成）
ok1_read:						! 继续读取标签
	seg cs						! 使用代码段前缀
	mov ax,sectors				! 将sectors的值加载到ax
	sub ax,sread				! 减去已读扇区数
	mov cx,ax					! 将ax的值加载到cx
	shl cx,#9					! 左移9位（乘以512）
	add cx,bx					! 加上bx
	jnc ok2_read				! 如果没有进位，跳转到ok2_read
	je ok2_read				! 如果相等，跳转到ok2_read
	xor ax,ax					! 将ax清零
	sub ax,bx					! 减去bx
	shr ax,#9					! 右移9位（除以512）
ok2_read:						! 计算读取扇区数标签
	call read_track				! 调用read_track函数读取磁道
	mov cx,ax					! 将ax的值加载到cx
	add ax,sread				! 加上已读扇区数
	seg cs						! 使用代码段前缀
	cmp ax,sectors				! 比较ax与sectors
	jne ok3_read				! 如果不相等，跳转到ok3_read
	mov ax,#1					! 将ax设置为1
	sub ax,head					! 减去当前磁头号
	jne ok4_read				! 如果不为0，跳转到ok4_read
	inc track					! 磁道号加1
ok4_read:						! 切换磁头标签
	mov head,ax					! 将ax的值保存到head
	xor ax,ax					! 将ax清零
ok3_read:						! 更新已读扇区数标签
	mov sread,ax				! 将ax的值保存到sread
	shl cx,#9					! 左移9位（乘以512）
	add bx,cx					! 加上cx
	jnc rp_read				! 如果没有进位，跳转到rp_read
	mov ax,es					! 将es的值加载到ax
	add ax,#0x1000				! 加上0x1000（下一个64KB段）
	mov es,ax					! 将ax的值保存到es
	xor bx,bx					! 将bx清零
	jmp rp_read				! 跳转到rp_read

read_track:						! 读取磁道的函数
	push ax						! 保存ax到栈
	push bx						! 保存bx到栈
	push cx						! 保存cx到栈
	push dx						! 保存dx到栈
	mov dx,track				! 将track的值加载到dx
	mov cx,sread				! 将sread的值加载到cx
	inc cx						! cx加1
	mov ch,dl					! 将dl的值加载到ch（磁道号）
	mov dx,head					! 将head的值加载到dx
	mov dh,dl					! 将dl的值加载到dh（磁头号）
	mov dl,#0					! 将dl设置为0（驱动器0）
	and dx,#0x0100				! 与操作，保留磁头位
	mov ah,#2					! 将ah设置为2（读扇区功能）
	int 0x13					! 调用BIOS中断0x13读取磁盘
	jc bad_rt					! 如果有进位（错误），跳转到bad_rt
	pop dx						! 从栈恢复dx
	pop cx						! 从栈恢复cx
	pop bx						! 从栈恢复bx
	pop ax						! 从栈恢复ax
	ret							! 返回
bad_rt:	mov ax,#0				! 错误处理标签，将ax设置为0
	mov dx,#0					! 将dx设置为0
	int 0x13					! 调用BIOS中断0x13重置磁盘
	pop dx						! 从栈恢复dx
	pop cx						! 从栈恢复cx
	pop bx						! 从栈恢复bx
	pop ax						! 从栈恢复ax
	jmp read_track				! 跳转到read_track重试

read_cursor:					! 读取光标位置的函数
	push ax						! 保存ax到栈
	push bx						! 保存bx到栈
	mov	ah,#0x03				! 将ah设置为0x03（读取光标位置功能）
	xor	bh,bh					! 将bh清零（页号0）
	int	0x10					! 调用BIOS中断0x10读取光标位置
	pop bx						! 从栈恢复bx
	pop ax						! 从栈恢复ax
	ret							! 返回

!/*
! * This procedure turns off the floppy drive motor, so
! * that we enter the kernel in a known state, and
! * don't have to worry about it later.
! */
kill_motor:						! 关闭软驱电机的函数
	push dx						! 保存dx到栈
	mov dx,#0x3f2				! 将dx设置为0x3f2（软驱控制端口）
	mov al,#0					! 将al设置为0（关闭电机）
	outb						! 输出字节到端口
	pop dx						! 从栈恢复dx
	ret							! 返回

sectors:						! 扇区数变量
	.word 0						! 初始化为0

msg1:							! 显示消息字符串
	.byte 13,10					! 回车换行符
	.ascii "Jnix is loading..."	! 字符串内容
	.byte 13,10,13,10			! 回车换行符

.org 508						! 设置当前位置为508字节
root_dev:						! 根设备号变量
	.word ROOT_DEV				! 初始化为ROOT_DEV
boot_flag:						! 引导标志
	.word 0xAA55				! 引导扇区结束标志

.text
endtext:						! 代码段结束标记
.data
enddata:						! 数据段结束标记
.bss
endbss:							! BSS段结束标记
